﻿/*
*	1-wire.c
*
*	Последнее изменение:	31.12.11 г.
*
*	Смотри 1-wire.h
*/

#include "1-wire.h"

/*
*	Послать сигнал сброса на шину
*
*	Принимает:	void
*	Возвращает:	unsigned char, где
*		0 - с шины пришёл сигнал о наличии на ней устройств;
*		1 - устройств на шине не обнаружено
*/
unsigned char ow_rst(void){
	// временная переменная для возвращаемого значения
	char ret;

	// устанавливаем линию на выход с низким уровнем
	sbi(_1w_ddr, _1w_dq_byt);
	cbi(_1w_prt, _1w_dq_byt);

	// формирование сигнала сброса
	_delay_us(500);

	// устанавливаем линию на вход (т.к. port=0 имеем режим Hi-Z на линии)
	cbi(_1w_ddr, _1w_dq_byt);

	// ожидание ответа от устройств
	_delay_us(60);

	// проверка ответа
	if (chb(_1w_pin, _1w_dq_byt)){ ret = 1; } else { ret = 0; }

	// приведение длительности процедуры к стандарному времени
	_delay_us(400);

	// возвращаем значение
	return ret;
}

/*
*	Сформировать на шине таймслот для записи единицы
*
*	Принимает:	void
*	Возвращает:	void
*/
void ow_wrt_l(void){
	// устанавливаем линию на выход с низким уровнем
	sbi(_1w_ddr, _1w_dq_byt);
	cbi(_1w_prt, _1w_dq_byt);

	// формирование таймслота
	_delay_us(120);

	// отпускаем линию
	cbi(_1w_ddr, _1w_dq_byt);

	// ожидаем до стандартной длительности таймслота
	_delay_us(5);
}

/*
*	Сформировать на шине таймслот для записи нуля
*
*	Принимает:	void
*	Возвращает:	void
*/
void ow_wrt_h(void){
	// устанавливаем линию на выход с низким уровнем
	sbi(_1w_ddr, _1w_dq_byt);
	cbi(_1w_prt, _1w_dq_byt);

	// формирование таймслота
	_delay_us(5);
	
	// отпускаем линию
	cbi(_1w_ddr, _1w_dq_byt);

	// ожидаем до стандартной длительности таймслота
	_delay_us(120);
}

/*
*	Сформировать на шине таймслот для чтения
*
*	Принимает:	void
*	Возвращает:	unsigned char, где
*		0 - чтение с шины нуля
*		1 - чтение с шины единицы
*/
unsigned char ow_read(void){
	// временная переменная для созвращаемого значения
	char ret;

	// устанавливаем линию на выход с низким уровнем
	sbi(_1w_ddr, _1w_dq_byt);
	cbi(_1w_prt, _1w_dq_byt);
	
	// формирование таймслота
	_delay_us(5);

	// отпускаем линию
	cbi(_1w_ddr, _1w_dq_byt);

	// ожидание ответа
	_delay_us(15);

	// чтение ответа
	if (chb(_1w_pin, _1w_dq_byt)){ ret = 1; } else { ret = 0; }

	// ожидаем до стандартной длительности таймслота
	_delay_us(105);

	// возвращаем значение
	return ret;	
}

/*
*	Послать на шину байт данных
*
*	Принимает:	uint8_t байт данных
*	Возвращает:	void
*/
void ow_snd_dat(uint8_t dat){
	// для каждого бита формируем соответствующий таймслот
	for(unsigned char i = 0; i < 8; i++){
		if (chb(dat, i)) { ow_wrt_h(); } else { ow_wrt_l(); }
	} // for
}

/*
*	Принять с шины байт данных
*
*	Принимает:	void
*	Возвращает: uint8_t байт данных
*/
uint8_t ow_get_byt(void){
	// временная переменная для возвращаемого значения
	uint8_t ret = 0x00;

	// формируем 8 таймслотов чтения и устанавливаем соответствующие единицы (нули уже есть по умолчанию)
	for(unsigned char i = 0; i < 8; i++){
		if (ow_read() == 1){ sbi(ret, i); }
	}

	// возвращаем значение
	return ret;
}

/*
*	Сканировать шину на предмет номеров устройств
*
*	Принимает:	void
*	Возвращает:	void
*
*	Функция использует:
*		1) предопределённую константу _1w_max_dev, которая содержит число устройств на шине;
*		2) массив _1w_dev[_1w_max_dev], в который записываются номера устройств.
*/
void ow_scan_bus(void){

	// временные переменные для хранения ответа устройств
	unsigned char b0, b1;

	// счётчик несоответствий
	unsigned char dm;

	/*
	*	Массив несоответствий.
	*	(!) Его теоритическая размерность (_1w_max_dev - 1);
	*	текущая размерность для упрощения алгоритма.
	*/
	unsigned char dismatch[_1w_max_dev];

	// устанавливаем значения всех элементов массива несоответствий в ноль
	for (unsigned char k = 0; k < _1w_max_dev; k++){
		dismatch[k] = 0;
	}

	// главный цикл
	for (unsigned char k = 0; k < _1w_max_dev; k++){

		// сбрасываем счётчик несоответствий
		dm = 0;

		// посылаем на шину сигнал сброса
		ow_rst();

		// посылаем на шину команду поиска номеров устройств
		ow_snd_dat(0xf0);
		
		/*
		*	Номер устройства состоит из 8 байт.
		*	Перебираем их все по очереди.
		*/
		for (unsigned char j = 0; j < 8; j++){
			// Считываем биты каждого байта
			for (unsigned char i = 0; i < 8; i++){
				// формируем два таймслота чтения
				b0 = ow_read();
				b1 = ow_read();		

				// всё устройства ответили нулём
				if ( (b0 == 0) && (b1 == 1) ){
			
					// записываем ноль в номер
					cbi(_1w_dev[k][j], i);
			
					// даём ответ устройствам
					ow_wrt_l();
			
				}
				// всё устройства ответили единицей
				else if ( (b0 == 1) && (b1 == 0) ){
					sbi(_1w_dev[k][j], i);
					ow_wrt_h();
				}
				/*
				*	Возникла неоднозначность в ответе.
				*	Здесь нужно проверить значение массива неоднозначностей, который по умолчанию заполнен нулями.
				*/
				else if ( (b0 == 0) && (b1 == 0) ){
					// если в массиве ноль
					if (dismatch[dm] == 0){

						// отправляем устройствам ноль, чтобы в поиске остались участвовать только с нулём в текущем бите
						ow_wrt_l();

						// записываем ноль в номер
						cbi(_1w_dev[k][j], i);

						// меняем значение массива на единицу, чтобы в следующем цикле опроса выбрать другой вариант
						dismatch[dm] = 1;

					// если в массиве единица -- аналогично
					} else {
						ow_wrt_h();
						sbi(_1w_dev[k][j], i);
						dismatch[dm] = 0;
					} // else
					// увеличиваем счётчик массива для сохранения следующей неоднозначности
					dm++;

				} // if
			} // for i
		} // for j
	} // for k
}

/*
*	Обратиться к определённому устройству на шине
*
*	Принимает: число от 0 до (_1w_max_dev-1), чтобы выбрать номер из массива _1w_dev.
*	Возвращает: void
*
*	Функция ow_scan_bus должна быть заранее вызвана для заполнения массива _1w_dev.
*/
void ow_match_rom(unsigned char num){
	// посылаем на шину сигнал сброса
	ow_rst();

	// посылаем на шину команду выбора устройства
	ow_snd_dat(0x55);

	// передаём соответствующий номер устройства
	for (unsigned char i = 0; i < 8; i++){
		ow_snd_dat( _1w_dev[num][i] );
	} // for
}
